package org.bpm.view
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import org.bpm.events.ProcessMapEvent;
	import org.bpm.models.MapLineModel;
	import org.bpm.models.MapModel;
	import org.bpm.models.MapNodeModel;
	
	/**
	 * The main view of the map, 
	 * this class creates nodes view and lines view.
	 * */
	public class Map extends Sprite
	{
		private var background:Sprite;
		private var nodes:Sprite;
		private var lines:Sprite;
		
		public var model:MapModel;
		
		public function Map(pModel:MapModel)
		{
			super();
			model = pModel;
			addEventListener(Event.ADDED_TO_STAGE,onAddToStage);
			addEventListener(Event.REMOVED_FROM_STAGE, onRemoveFromStage);
		}
		private function onAddToStage(pEvent:Event):void{
			removeEventListener(Event.ADDED_TO_STAGE, onAddToStage);
			create();
		}
		private function onRemoveFromStage(pEvent:Event):void{
			removeEventListener(Event.REMOVED_FROM_STAGE, onRemoveFromStage);
			if(background){
				removeChild(background);
				background = null;
			}
			removeNodes();
			removeLines();
			model = null;
		}
		private function removeNodes():void{
			if(nodes){
				for(var i:int=0;i<nodes.numChildren;i++){
					var node:MapNode = nodes.getChildAt(i) as MapNode;
					node.removeEventListener(ProcessMapEvent.MAP_ELEMENT_CLICK, mouseEvent);
					node.removeEventListener(ProcessMapEvent.MAP_ELEMENT_OVER, mouseEvent);
					node.removeEventListener(ProcessMapEvent.MAP_ELEMENT_OUT, mouseEvent);
				}
				removeChild(nodes);
				nodes = null;
			}
		}
		private function removeLines():void{
			if(lines){
				for(var i:int=0;i<lines.numChildren;i++){
					var line:MapLine = lines.getChildAt(i) as MapLine;
					line.removeEventListener(ProcessMapEvent.MAP_ELEMENT_CLICK, mouseEvent);
					line.removeEventListener(ProcessMapEvent.MAP_ELEMENT_OVER, mouseEvent);
					line.removeEventListener(ProcessMapEvent.MAP_ELEMENT_OUT, mouseEvent);
				}
				removeChild(lines);
				lines = null;
			}
		}
		/**
		 * This method has to be involved AFTER added to stage,
		 * because we need use hitTestPoint to check the arrow lines and node views.
		 * */
		private function create():void{
			x = model.rect.x;
			y = model.rect.y;
			
			createBackground();
			
			createNodes();
			createLines();
		}
		private function createBackground():void{
			background = new Sprite();
			background.graphics.beginFill(0,0);
			background.graphics.drawRect(0,0,model.rect.width,model.rect.height);
			addChild(background);
		}
		
		private function createNodes():void{
			nodes = new Sprite();
			addChild(nodes);
			
			for(var i:int=0;i<model.nodes.length;i++){
				var node:MapNode = new MapNode(model.nodes[i]);
				node.addEventListener(ProcessMapEvent.MAP_ELEMENT_CLICK, mouseEvent);
				node.addEventListener(ProcessMapEvent.MAP_ELEMENT_OVER, mouseEvent);
				node.addEventListener(ProcessMapEvent.MAP_ELEMENT_OUT, mouseEvent);
				nodes.addChild(node);
			}
		}
		private function mouseEvent(pEvent:ProcessMapEvent):void{
			dispatchEvent(pEvent.clone());
		}
		
		private function createLines():void{
			lines = new Sprite();
			/**
			 * lines should be beneath the nodes.
			 * every line has a couple of nodes which define the start and end points.
			 * */
			addChildAt(lines,1);
			for(var i:int = 0; i<model.nodes.length;i++){
				var nodeModel:MapNodeModel = model.nodes[i] as MapNodeModel;
				for(var j:int=0;j<nodeModel.transitions.length;j++){
					var lineModel:MapLineModel = nodeModel.transitions[j] as MapLineModel;
					var line:MapLine = new MapLine(lineModel);
					line.addEventListener(ProcessMapEvent.MAP_ELEMENT_CLICK, mouseEvent);
					line.addEventListener(ProcessMapEvent.MAP_ELEMENT_OVER, mouseEvent);
					line.addEventListener(ProcessMapEvent.MAP_ELEMENT_OUT, mouseEvent);
					
					line.createByNodes(getMapNodeById(lineModel.startNodeId), getMapNodeById(lineModel.endNodeId));
					lines.addChild(line);
				}
			}
		}
		
		private function getMapNodeById(pId:String):MapNode{
			for(var i:int=0;i<nodes.numChildren;i++){
				var node:MapNode = nodes.getChildAt(i) as MapNode;
				if(node.model.id == pId) return node;
			}
			return null;
		}
	}
}